/*
 * @Author James Metting
 */

import javax.media.opengl.*;
import javax.swing.*;

import com.sun.opengl.util.GLUT;
import com.sun.opengl.util.j2d.TextRenderer;
import java.awt.*;

import java.lang.Math;

public class PuzzlePiece {
	Coordinate location;
	private PuzzleColor color;
	//0,1,2,3 = up,down,left,right
	PuzzlePiece[] neighbors;
	PuzzleCut[] cuts;
	static float size = .5f;
	//distance to connect
	static float distance =.1f;
	String[] text;
	PuzzlePiece[] attached;
	//where the box was clicked on to drag right
	private float displaceX =0;
	private float displaceY=0;
	boolean updated;
	private int labelLevel=1;
	String rawString;

	public PuzzlePiece(Coordinate location,PuzzleColor col,String text,int level)
	{
		this.location = location;
		neighborsNull();
		color = col;
		labelLevel = level;
		rawString ="text";
		setText(text);
	}
	/**
	 * sets the text for this puzzle piece
	 * puts it into appropriate lines for the level number
	 * makes sure that it dosent go over the edge
	 * unless it needs to
	 * @param t
	 */
	public void setText(String s)
	{
		String[] split = s.split(" ");
		String[] result = new String[0];
		int length = (labelLevel *5)+5;
		int resIndex = 0;//index of the result array
		String temp = "";//temporary string that holds the next line's values
		int i = 0;
		for(i=0; i < split.length;i++)
		{
			temp +=split[i]+" ";
			if(temp.length()>(length+1))//plus 1 to include extra space on end
			{
				resIndex++;
				String[] tempArray = result;
				result = new String[resIndex];
				for (int x = 0; x<resIndex-1;x++)
				{
					result[x]=tempArray[x];
				}

				temp  = temp.substring(0,(temp.length()-(split[i].length()+1)));//gets rid of the last word
				i--;
				if(temp.length() <=1)
				{
					i++;
					temp = split[i];
				}
				result[resIndex-1] = temp;
				temp ="";
			}
		}
		if(temp.length()<=(length+1))
		{
			resIndex++;
			String[] tempArray = result;
			result = new String[resIndex];
			for (int x = 0; x<resIndex-1;x++)
			{
				result[x]=tempArray[x];
			}
			result[resIndex-1] = temp;
		}
		text = result;
	}
	/**
	 * gets the text from this puzzle piece
	 * @return
	 */
	public String getText()
	{
		String result = "";
		for(int i = 0; i < text.length;i++)
		{
			result += text[i];
		}
		return result;
	}
	/**
	 * changes the level of the piece and changes the string appropriately
	 * @param level
	 */
	public void setLevel(int level)
	{
		labelLevel = level;
		setText(rawString);
	}
	/**
	 * returns the piece level
	 * @return
	 */
	public int getLevel()
	{
		return labelLevel;
	}
	/**
	 * a color for this piece as specified by the color parameter
	 * @param col
	 */
	public void setColor(PuzzleColor col)
	{
		color = col;
	}
	/**
	 *
	 * sets where the piece is by using the location reference
	 * a piece can move without knowing about the piece.
	 * @param location
	 */
	public void setLocation(Coordinate location)
	{
		this.location = location;

	}
	/**
	 * Sets all of the cuts given an array of cuts size/length 4
	 * @param cuts
	 */
	public void setCuts(PuzzleCut[] cuts)
	{
		this.cuts=cuts;
	}
	/**
	 * adds a cut to one of the sides
	 * Locations:
	 * _______
	 * |  0  |
	 * |2   3|
	 * |  1  |
	 * _______
	 * @param index
	 * @param cut
	 */
	public void addCut(int index,PuzzleCut cut)
	{
		cuts[index]=cut;
	}
	/**
	 * nullifys all of the neighbors so
	 * they can be set at a later time
	 *
	 */
	public void neighborsNull()
	{
		neighbors = new PuzzlePiece[4];
		attached = new PuzzlePiece[4];
		cuts= new PuzzleCut[4];
		for (int i = 0; i < 4; i++)
		{
			neighbors[i]=null;
			cuts[i]=null;
			attached[i]=null;
		}
		updated = false;
	}
	/**
	 * sets the neighbor given the index in the location specified
	 * by the addCut() comments
	 * @param index
	 * @param neighbor
	 */
	public void setNeighbor(int index,PuzzlePiece neighbor)
	{
		neighbors[index]=neighbor;
	}
	/**
	 * sets the neighbor given the index in the location specified
	 * by the addCut() comments
	 * @param index
	 * @param neighbor
	 * @param cut > shares the cut paameters
	 */
	public void setNeighbor(int index,PuzzlePiece neighbor,PuzzleCut cut)
	{
		//adds the neighbor here and cut
		neighbors[index]=neighbor;
		cuts[index]=cut;
		//adds this to the other attached and the inverted cut
		switch(index){//inverts directions for other pieces
		case 0:neighbor.setNeighbor(1,this);neighbor.addCut(1,cut.clone(true));break;
		case 1:neighbor.setNeighbor(0,this);neighbor.addCut(0,cut.clone(true));break;
		case 2:neighbor.setNeighbor(3,this);neighbor.addCut(3,cut.clone(true));break;
		case 3:neighbor.setNeighbor(2,this);neighbor.addCut(2,cut.clone(true));break;
		}
	}
	/**
	 * checks the neighbors to see if they are close enough to attach
	 *if they are then they attach by entering an array of attached pieces
	 *and this list is called when displaying
	 */
	public void checkNeighbor()
	{

		//checks to see if it has a neighbor
		//and if it's already been added
		for(int i = 0; i<4;i++)
		{
			if (neighbors[i]!=null&&neighbors[i]!=attached[i])
			{
				float x = location.x;
				float y = location.y;
				//neighbors x and y
				float nx =neighbors[i].location.x;
				float ny =neighbors[i].location.y;
				float halfSize = (size/2);
				//point to check / edges
				if(i==0)
				{
					x += halfSize;
					y+=size;
					nx+= halfSize;
				}
				else if(i==1)
				{
					x += halfSize;
					nx += halfSize;
					ny += size;
				}
				else if(i==2)
				{
					y += halfSize;
					nx+=size;
					ny+=halfSize;
				}
				else
				{
					ny += halfSize;
					x+=size;
					y+=halfSize;
				}
				//relation check
				//may need to be fixed later (works fine on first tests)
				if(Math.abs(nx-x)<= distance && Math.abs(ny-y)<= distance)
				{
					//checks to see if attached are in the correct direction
					if(neighbors[0] != null&&neighbors[i]!=null)
						if(neighbors[0].neighbors[1] == this)
							addAttached(i);
					if(neighbors[1] != null&&neighbors[i]!=null)
						if(neighbors[1].neighbors[0] == this)
							addAttached(i);
					if(neighbors[2] != null&&neighbors[i]!=null)
						if(neighbors[2].neighbors[3] == this)
							addAttached(i);
					if(neighbors[3] != null&&neighbors[i]!=null)
						if(neighbors[3].neighbors[2] == this)
							addAttached(i);
				}
			}
		}
	}
	/**
	 * attaches a piece from the parameter
	 * turns off the appropriate cuts
	 * @int index
	 */
	private void addAttached(int index)
	{

		attached[index]=neighbors[index];
		cuts[index]=null;

		//makes sure they both know about each other
		switch(index){
		case 0:attached[index].addAttached(1,this);attached[index].addCut(1,null);break;
		case 1:attached[index].addAttached(0,this);attached[index].addCut(0,null);break;
		case 2:attached[index].addAttached(3,this);attached[index].addCut(3,null);break;
		case 3:attached[index].addAttached(2,this);attached[index].addCut(2,null);break;
		}
	}
	/**
	 * adds a specified peice to the list
	 * @param index
	 * @param piece
	 */
	public void addAttached(int index,PuzzlePiece piece)
	{
		attached[index]=piece;
	}
	/**
	 * displays this piece every time it's called
	 *
	 * @param gl
	 * @param backColor
	 */
	public void display(GLAutoDrawable drawable,PuzzleColor backColor)
	{
		GL gl = drawable.getGL();
		gl.glPushMatrix();
		gl.glClear(GL.GL_DEPTH_BUFFER_BIT);
		gl.glColor3f(color.getRed(), color.getGreen(), color.getBlue());

		//SQUARE -draws the base square in the above color
		gl.glBegin( GL.GL_QUADS );
		gl.glVertex3f( location.x,location.y, 0 );
		gl.glVertex3f( location.x + size, location.y, 0 );
		gl.glVertex3f( location.x+size, location.y+size, 0 );
		gl.glVertex3f( location.x, location.y+size, 0 );
		//display text
		gl.glMatrixMode(GL.GL_MODELVIEW);
		//emitString(drawable,text, (float)(location.x+size-.05), (float)(location.y+.05));
		//PuzzleApplet.emitString(drawable,text, (float)(location.x+size-.05), (float)(location.y+.05));
		gl.glEnd();
		gl.glPopMatrix();
		for(int i = 0; i < 4; i ++)
		{
			if(cuts[i]!=null)
			{
				Coordinate temp;
				float halfSize = size/2;
				switch(i)
				{

					case 0:temp = new Coordinate(location.x+halfSize,location.y+size,0);break;
					case 1:temp = new Coordinate(location.x+halfSize,location.y,0);break;
					case 2:temp = new Coordinate(location.x,location.y+halfSize,0);break;
					case 3:temp = new Coordinate(location.x+size,location.y+halfSize,0);break;
					default:temp = new Coordinate(0,0,0);break;
				}
				cuts[i].display(gl,color,backColor,temp,location,i);
			}
		}
	}
	/**
	 * updates to attach all nodes starting with this one
	 *
	 */
	public void updateAttached()
	{
		if(!updated)
		{
			updated = true;//update stops recursion
			for(int i = 0;i<4;i++)
			{
				if(attached[i]!=null)
				{
					//error
					try
					{
					switch(i){
					case 0:neighbors[0].location.y = (location.y+size);neighbors[0].location.x = location.x;break;
					case 1:neighbors[1].location.y = (location.y-size);neighbors[1].location.x = location.x;break;
					case 2:neighbors[2].location.x = (location.x-size);neighbors[2].location.y = location.y;break;
					case 3:neighbors[3].location.x = (location.x+size);neighbors[3].location.y = location.y;break;
					}
					}
					catch(NullPointerException n)
					{}
					attached[i].updateAttached();
				}

			}
		}

	}
	/**
	 * Rotates the piece and all that are attached clockwise
	 *
	 */
	public void rotateClock()
	{
		if(!updated)
		{
			updated = true;//update stops recursion

			//connected attached and cuts all rotate
			PuzzlePiece tempatt =attached[0];
			PuzzlePiece tempneigh = neighbors[0];
			PuzzleCut tempcut = cuts[0];
			attached[0] =attached[2];
			neighbors[0] = neighbors[2];
			cuts[0]= cuts[2];
			attached[2] =attached[1];
			neighbors[2] = neighbors[1];
			cuts[2]= cuts[1];
			attached[1] =attached[3];
			neighbors[1] = neighbors[3];
			cuts[1]= cuts[3];
			attached[3] =tempatt;
			neighbors[3] = tempneigh;
			cuts[3]= tempcut;
			for(int i = 0;i<4;i++)
			{
				if(attached[i]!=null)
				{
					attached[i].rotateClock();
				}

			}
		}
	}
	/**
	 * Rotates the piece and all that are attached counter clockwise
	 *
	 */
	public void rotateCounter()
	{
		if(!updated)
		{
			updated = true;//update stops recursion

			//connected attached and cuts all rotate
			PuzzlePiece tempatt =attached[0];
			PuzzlePiece tempneigh = neighbors[0];
			PuzzleCut tempcut = cuts[0];
			attached[0] =attached[3];
			neighbors[0] = neighbors[3];
			cuts[0]= cuts[3];
			attached[3] =attached[1];
			neighbors[3] = neighbors[1];
			cuts[3]= cuts[1];
			attached[1] =attached[2];
			neighbors[1] = neighbors[2];
			cuts[1]= cuts[2];
			attached[2] =tempatt;
			neighbors[2] = tempneigh;
			cuts[2]= tempcut;
			for(int i = 0;i<4;i++)
			{
				if(attached[i]!=null)
				{
					attached[i].rotateCounter();
				}

			}
		}
	}
	/**
	 * sets the updated flag
	 * @param up
	 */
	public void setUpdated(boolean up)
	{
		updated = up;
	}
	/**
	 * figures out if the mouse is in reach of this puzzle piece
	 * based on the integer values of
	 * the XY pixal location of the mouse
	 * @param x
	 * @param y
	 */
	public boolean isMovable(int x, int y)
	{
		boolean result = false;
		//checks if mouse is within this box
		if(translateX(x)<(location.x+size) &&
				translateY(y)<(location.y+size) &&
				translateX(x)>location.x &&
				translateY(y)>location.y)
		{
			displaceX=translateX(x)-location.x;
			displaceY=translateY(y)-location.y;
			result =true;
		}
		return result;
	}
	/**
	 * moves the piece based on the integer values of
	 * the XY pixal location of the mouse
	 * @param x
	 * @param y
	 */
	public void setLocation(int x, int y)
	{
		//sets based on displacement of X and Y
		location.x = translateX(x)-displaceX;
		location.y = translateY(y)-displaceY;
		float showing=.05f;//how much in -1 -1 units has to be showning
		if((location.x+showing) >1||
				(location.x+size-showing) <-1||
				(location.y+showing) >1||
				(location.y+size-showing) <-1)
		{
			location.x = 0;
			location.y = 0;
		}
	}
	//this portion can be more efficient if
	//executed only once per click
	/**
	 * translates to GL grid
	 * @param x
	 * @return
	 */
	public float translateX(int x)
	{

		float halfX = (PuzzleApplet.getXval()/2);
		float result = (x-(halfX))/halfX;
		return result;
	}
	/**
	 * translates to GL GRid
	 * @param y
	 * @return
	 */
	public float translateY(int y)
	{
		float halfY = (PuzzleApplet.getYval()/2);
		float result = ((y-(halfY))/halfY)*-1;
		return result;
	}


}
